﻿using System;
using System.Data;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using PubSubWeb.Model;

namespace PubSubWeb.Container
{
    public class SubscriptionManager
    {
        private InMemorySubscriptionStore subscriptionStore;

        private static SubscriptionManager instance;

        public SubscriptionManager()
        {
            this.subscriptionStore = new InMemorySubscriptionStore();
        }

        public static SubscriptionManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new SubscriptionManager();
                }

                return instance;
            }
        }

        public void Subscribe(
                        string callback, 
                        string topic, 
                        SubscriptionVerificationType verificationType, 
                        int leaseSeconds, 
                        string secret, 
                        string verifyToken)
        {
            Subscription subscription = this.subscriptionStore.AddSubscription(
                                                                        callback,
                                                                        topic,
                                                                        verificationType,
                                                                        leaseSeconds,
                                                                        secret,
                                                                        verifyToken);

            //Now verify the subscription
            this.VerifySubscription(subscription, SubscriptionMode.Subscribe);
        }

        private void VerifySubscription(Subscription subscription, SubscriptionMode mode)
        {
            VerificationAsyncState asyncState = new VerificationAsyncState(subscription);            
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(
                                                    this.GetVerificationUrl(
                                                                        subscription, 
                                                                        mode,
                                                                        asyncState.Challenge));

            subscription.VerificationState = VerificationState.InProgress;
            webRequest.BeginGetResponse(
                                new AsyncCallback(VerifyResponseReceived),
                                asyncState);
        }

        private void VerifyResponseReceived(IAsyncResult ar)
        {
            lock (this)
            {
                VerificationAsyncState asyncState = (VerificationAsyncState)ar.AsyncState;
                HttpWebResponse response = (HttpWebResponse)asyncState.Request.EndGetResponse(ar);

                if (response.StatusCode == HttpStatusCode.Accepted)
                {
                    Stream responseStream = response.GetResponseStream();
                    byte[] buffer = new byte[responseStream.Length];

                    responseStream.Read(buffer, 0, buffer.Length);

                    if (asyncState.Challenge == buffer.ToString())
                    {
                        asyncState.Subscription.VerificationState = VerificationState.Verified;
                    }
                    else
                    {
                        asyncState.Subscription.VerificationState = VerificationState.Failed;

                        this.subscriptionStore.RemoveSubscription(
                                                            asyncState.Subscription.CallbackURL,
                                                            asyncState.Subscription.TopicURL,
                                                            asyncState.Subscription.VerificationType,
                                                            asyncState.Subscription.Secret,
                                                            asyncState.Subscription.VerifyToken);
                    }
                }
                else
                {
                    asyncState.Subscription.VerificationState = VerificationState.Rejected;

                    this.subscriptionStore.RemoveSubscription(
                                                        asyncState.Subscription.CallbackURL,
                                                        asyncState.Subscription.TopicURL,
                                                        asyncState.Subscription.VerificationType,
                                                        asyncState.Subscription.Secret,
                                                        asyncState.Subscription.VerifyToken);
                }
            }
        }

        private string GetVerificationUrl(Subscription subscription, SubscriptionMode mode, string challenge)
        {
            string url = String.Format(
                                "{0}&hub.mode={1}&hub.topic={2}&hub.challenge={3}&hub.lease_seconds={4}",
                                subscription.CallbackURL,
                                (mode == SubscriptionMode.Subscribe)?"subscribe":"unsubscribe",
                                subscription.TopicURL,
                                challenge,
                                (subscription.ExpiryTime - DateTime.Now).TotalSeconds);

            if (!String.IsNullOrEmpty(subscription.VerifyToken))
            {
                url += ("&hub.verify_token=" + subscription.VerifyToken);
            }

            return url;
        }

        public void Unsubscribe(
                        string callback, 
                        string topic, 
                        SubscriptionVerificationType verificationType, 
                        string secret, 
                        string verifyToken)
        {
            this.subscriptionStore.RemoveSubscription(
                                                callback,
                                                topic,
                                                verificationType,
                                                secret,
                                                verifyToken);
        }

        public void Publish(string topic)
        {
        }
    }
}
